home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
GameKit
/
Examples
/
PacMan
/
Player.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
7KB
|
216 lines
// Handles moving and rendering the Pac, whether under player or demo control.
#import <libc.h>
#import "Player.h"
#import "Monster.h"
#import "Maze.h"
#import "PacManView.h"
#import <gamekit/gamekit.h>
#import <appkit/appkit.h>
#import "PacMovement.h"
// used to translate PAC_<direction> to px, py velocities
static int pacxvec[9] = { 0, 2, -2, 0, 0, 0, 0, 0, 0 };
static int pacyvec[9] = { 0, 0, 0, 0, -2, 0, 0, 0, 2 };
//static int opdir[9] = { 0, 2, 1, 0, 8, 0, 0, 0, 4 };
static int bothdir[9] = { 0, 3, 3, 0, 12, 0, 0, 0, 12 };
// the y-coord in the Pacs.tiff image indexed by direction
static int pacypic[9] = { 0, 5 * PAC_WIDTH, 4 * PAC_WIDTH, 0,
6 * PAC_WIDTH, 0, 0, 0, 7 * PAC_WIDTH };
static int pacydie[9] = { 0, 1 * PAC_WIDTH, 0, 0,
2 * PAC_WIDTH, 0, 0, 0, 3 * PAC_WIDTH };
@implementation Player
- init // initialize the player
{
[super init];
pacs[1] = [NXImage findImageNamed:"Pacs.tiff"];
pacs[2] = [NXImage findImageNamed:"PacsBig.tiff"];
nextDir = PAC_STOP;
curDir = PAC_STOP;
playerStopped = NO;
return self;
}
- (BOOL)newPlayer // get and set up a new Pac. Returns NO if can't
{
if (![pacsLeft decNumUp:self]) return NO; // no pacs left
[self resetPlayer];
return YES;
}
- resetPlayer // reset all player info
{
curDir = PAC_RIGHT;
nextDir = PAC_RIGHT;
px = 0; py = 0;
state = PAC_ALIVE;
cycle = 0;
lastx = - 4 * GHOST_SIZE;
lasty = - 4 * GHOST_SIZE;
playerStopped = NO;
[maze playerPosition:&myX :&myY];
return self;
}
- (BOOL)pacAlive // returns YES if Pac is alive
{
if (state < PAC_ALIVE) return NO;
return YES;
}
- demoMove:sender // handles Pac movement during the demo sequence
{
int i, zz; float xx, yy; int zi = 1;
int ghost_x = 0;
int ghost_y = 0;
int closest = (BLOCK_WIDTH + BLOCK_HEIGHT) * GHOST_SIZE;
register int tdir = 0x0f;
register int sense = 0;
int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
// keep us in the track between maze walls
if (dx) tdir &= 0x03;
if (dy) tdir &= 0x0c;
for (i=0; i<4; i++) { // find closest ghost; run away from it.
[[sender ghost:i] at:&xx :&yy];
zz = abs(xx - myX) + abs(yy - myY);
if (zz < closest) {
zi = i;
closest = zz;
ghost_x = xx;
ghost_y = yy;
} }
// first, find the directions in which pac can go
if ((abs(ghost_x - myX) >= GHOST_SIZE * 2 - 2) ||
(abs(ghost_y - myY) >= GHOST_SIZE * 2 - 2)) {
if ([maze playerWall:(myX+GHOST_SIZE) :myY] || (px < 0))
tdir &= ~0x01;
if ([maze playerWall:(myX-1) :myY] || (px > 0)) tdir &= ~0x02;
if ([maze playerWall:myX :(myY-1)] || (py > 0)) tdir &= ~0x04;
if ([maze playerWall:myX :(myY+GHOST_SIZE)] || (py < 0))
tdir &= ~0x08;
if ((random() & 0x0f) > 10) {
// worry about shorter distance first.
if (abs(ghost_x - myX) < abs(ghost_y - myY)) {
if (tdir & 0x03) tdir &= 0x03;
} else {
if (tdir & 0x0c) tdir &= 0x0c;
} }
} else { // if a ghost is close by, we can reverse direction.
if ([maze playerWall:myX :(myY-1)]) tdir &= ~0x04;
if ([maze playerWall:myX :(myY+GHOST_SIZE)]) tdir &= ~0x08;
if ([maze playerWall:(myX+GHOST_SIZE) :myY]) tdir &= ~0x01;
if ([maze playerWall:(myX-1) :myY]) tdir &= ~0x02;
}
// now choose the new direction for the pac
if ([[sender ghost:zi] canBeEaten]) {
// chase an eatable ghost
sense = demofind[sgn(ghost_y - myY) + 1][sgn(ghost_x - myX) + 1];
px = chasexvec[tdir][sense];
py = chaseyvec[tdir][sense];
} else {
if (((abs(ghost_x - myX) <= GHOST_SIZE * 2 - 2) &&
(abs(ghost_y - myY) <= GHOST_SIZE * 2 - 2)) ||
((random() & 0x0f) > 2)) { // if close or random allows, go
// according to program
sense = demofind[sgn(ghost_y - myY) + 1][sgn(ghost_x - myX) + 1];
} else { // random direction occasionally
sense = random() & 0x07;
}
px = demoxvec[tdir][sense];
py = demoyvec[tdir][sense];
} // set up the direction:
curDir = demoDir[sgn(py) + 1][sgn(px) + 1];
return self;
}
- move:sender // Move the PacMan one animation frame
{
int dx = myX % PAC_WIDTH; int dy = myY % PAC_WIDTH;
register int tdir = 0x0f;
if (state < PAC_DEAD) { // Pac is dying
state++;
} else if (state >= PAC_ALIVE) { // Pac is alive, so move it
if ([gameView demoMode]) {
// if demo mode, we move the pac ourselves
[self demoMove:sender];
} else {
// if not demo mode, we let the user decide how to move the pac.
// first, find the directions in which the pac can go
if (dx || dy) tdir = bothdir[curDir]; // curr. dir or opp.
// direction are allowed; other dir. only at junctions.
if ([maze playerWall:(myX+PAC_WIDTH) :myY]) tdir &= ~PAC_RIGHT;
if ([maze playerWall:(myX-2) :myY]) tdir &= ~PAC_LEFT;
if ([maze playerWall:myX :(myY-2)]) tdir &= ~PAC_DOWN;
if ([maze playerWall:myX :(myY+PAC_WIDTH)]) tdir &= ~PAC_UP;
// see if we can go the "next" direction the player wants or not...
if (nextDir & tdir) { // we can!
curDir = nextDir;
}
if (playerStopped) tdir = 0x00; // don't move if stopped
// now choose the new direction for the pac
// the "&" makes sure we stop if we can't go further in this dir.
px = pacxvec[curDir & tdir];
py = pacyvec[curDir & tdir];
}
} else { // Pac is dead so do nothing.
px = 0; py = 0;
}
return self;
}
- newDirection:(int)newDir // send Pac in new direction.
{
if (newDir == PAC_STOP) {
playerStopped = YES;
} else {
nextDir = newDir;
playerStopped = NO;
}
return self;
}
- pacDie { state = PAC_DYING; return self; } // the pac will melt
- renderAt:(int)posx :(int)posy move:(BOOL)moveOk // draw pac
// you should lock focus on view that gets the Pac first.
{
NXRect from;
NXPoint pos;
[super renderAt:posx :posy move:moveOk];
pos.x = myX * scale + posx; pos.y = myY * scale + posy;
if (state == PAC_ALIVE) { // draw living pac
NXSetRect(&from, cycle * PAC_WIDTH * scale, pacypic[curDir] * scale,
PAC_WIDTH * scale, PAC_WIDTH * scale);
} else if (state < PAC_DEAD) { // draw dying pac
NXSetRect(&from, (state - PAC_DYING) * PAC_WIDTH * scale,
pacydie[curDir] * scale, PAC_WIDTH * scale, PAC_WIDTH * scale);
if (!cycle) state++;
if (cycle == 1) cycle = -1; // change period while we die
} else { // if dead, draw no pac (upper rt. of image is blank)
NXSetRect(&from, 10 * PAC_WIDTH * scale, 7 * PAC_WIDTH * scale,
PAC_WIDTH * scale, PAC_WIDTH * scale);
}
[pacs[scale] composite:NX_SOVER fromRect:&from toPoint:&pos];
cycle++; if (cycle > 5) cycle = 0; // cycle through six images.
return self;
}
@end